#!/usr/bin/env python3

#             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
#                     Version 2, December 2004
#
#  Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
#
#  Everyone is permitted to copy and distribute verbatim or modified
#  copies of this license document, and changing it is allowed as long
#  as the name is changed.
#
#             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
#    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
#
#   0. You just DO WHAT THE FUCK YOU WANT TO.

import argparse
import numpy as np
from random import randint as rnd


def parse_args():
    parser = argparse.ArgumentParser(description='Adnmb Mine Game Generater')

    parser.add_argument('height', type=int, default=16,
                        help='Height of each board')

    parser.add_argument('width', type=int, default=16,
                        help='Width of each board')

    parser.add_argument('mine', type=int, default=10, help='Num of mines')

    parser.add_argument('--num', dest='num', type=int, default=1,
                        help='Num of board')

    parser.add_argument('--hints', dest='hints', type=int, default=2,
                        help='Nums of \'0\' blocks visible in each \
                        board when game start')
    parser.add_argument('--side-by-side', dest='sbs', action='store_true',
                        help='Display in 2 columns, width should be less than \
                        9 and num should be even.')
    return parser.parse_args()


def check_args(ARGS):
    assert ARGS.width <= 16
    if ARGS.sbs:
        assert ARGS.width <= 8
        assert ARGS.num % 2 == 0
    assert ARGS.mine <= ARGS.height * ARGS.width
    assert ARGS.hints <= ARGS.height * ARGS.width - ARGS.mine


class Mine:
    chrt = ['▓', '１', '２', '３', '４', '５', '６', '７', '８', '卐']
    hide_mark_l = '[h]'
    hide_mark_r = '[/h]'

    def __init__(self, shape, mine=None):
        self.h, self.w = shape[0], shape[1]
        self.mapsize = self.h * self.w
        if mine >= self.mapsize:
            print('Mine is more than mapsize')
            exit()
        if mine is None:
            mine = self.h * self.w // 6
        self.mine = mine
        self.mine_loc = []
        self.map = np.zeros(shape, dtype=np.int32)
        self.mark()

    def mark(self):
        for i in range(self.mine):
            while True:
                m_h = rnd(0, self.h-1)
                m_w = rnd(0, self.w-1)
                if self.map[m_h][m_w] != 9:
                    self.map[m_h][m_w] = 9
                    self.mine_loc.append([m_h, m_w])
                    for m in range(m_h-1, m_h+2):
                        for n in range(m_w-1, m_w+2):
                            if m < 0 or m >= self.h or n < 0 or n >= self.w \
                                    or self.map[m][n] == 9:
                                continue
                            self.map[m][n] += 1
                    break

    def draw(self, dmap=None, easy_start=0):
        if dmap is None:
            dmap = self.map
        if easy_start != 0:
            zeros = []
            for h in range(0, self.h-1):
                for w in range(0, self.w-1):
                    if dmap[h][w] == 0:
                        zeros.append((h, w), )
            for j in np.random.choice(list(range(len(zeros))),
                                      easy_start,
                                      replace=False):
                e_h, e_w = zeros[j]
                dmap[e_h][e_w] = -1

        for i in dmap:
            for r in i:
                if r == -1:
                    print(f'{self.chrt[0]} ', end='')
                else:
                    print(f'{self.hide_mark_l}', end='')
                    print(f'{self.chrt[r]}', end='')
                    print(f'{self.hide_mark_r} ', end='')
            print('')

    def draw_side_by_side(self, map1, map2):
        '''print 2 game vertically side by side'''
        if len(map1) != len(map2):
            print('map1 and map2 have different height')
            exit()
        for i in range(len(map1)):
            for j in map1[i]:
                print(f'{self.hide_mark_l}{self.chrt[j]}{self.hide_mark_r} ',
                      end='')
            print('│ ', end='')
            for k in map2[i]:
                print(f'{self.hide_mark_l}{self.chrt[k]}{self.hide_mark_r} ',
                      end='')
            print('')


def generate(ARGS):
    if ARGS.sbs:
        for i in range(ARGS.num//2):
            my_mine0 = Mine(shape=(ARGS.height, ARGS.width), mine=ARGS.mine)
            my_mine1 = Mine(shape=(ARGS.height, ARGS.width), mine=ARGS.mine)
            my_mine0.draw_side_by_side(my_mine0.map, my_mine1.map)
            print('─'*21)
    else:
        for i in range(ARGS.num):
            my_mine1 = Mine(shape=(ARGS.height, ARGS.width), mine=ARGS.mine)
            my_mine1.draw(easy_start=ARGS.hints)
            print('─'*21)


if __name__ == '__main__':
    ARGS = parse_args()
    check_args(ARGS)
    generate(ARGS)
